package Ui;


import DBase.Ships.Ship;
import DBase.Coordinate;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import java.io.File;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;


/**
 * 
 * @author Elya & Dana
 */
public class GameUiXML extends GameUi{
    

    private int numPlayers = 0; //number of players counter
    private String [] playerNames;
    private int [] playerTypes;
    private String [] shipsNames ;
    private int [] shipSizesArray;
    private Ship [][] shipsLocations;

    /**
     * 
     */
    public GameUiXML() {
   
        
        ReadBoardFromXml();
    }
    
    
    /**
     * Gets the number of players
     * @return numPlayers
     */
    @Override
    public  int getPlayersNumber(){return numPlayers;}
    
    /**
     * Gets the array of player names
     * @param numPlayers the number of players
     * @return playerNames - the string array of players
     */
    @Override
    public String [] getPlayersNames(int numPlayers){return playerNames;}
    
    /**
     * Get the array of player types
     * @param numPlayers the number of players
     * @return playerTypes-the array of player types
     */
    @Override
    public  int [] getPlayersType(int numPlayers){return playerTypes;}
    
    /**
     * Gets the array of ship names
     * @return  shipsNames - the array of ship names
     */
    @Override
    public String [] getShipsData(int [] shipSizesArray){
        System.arraycopy(this.shipSizesArray, 0, shipSizesArray, 0, shipSizesArray.length);
        return shipsNames;
    }

    
    
    /**
     * Places a ship on a players board
     * @param x row
     * @param y column
     * @param direction Horizontal or Vertical
     * @param currentShipSize the ship size
     * @param PlayerName the players name which we want to place the ships for
     * @param shipName the ship's name
     */
    @Override
    public void PlaceShipsOnBoard(int [] x, int [] y,int [] direction,int currentShipSize,String PlayerName,String shipName){
            


        int indexOfPlayer=0;
        
        for(int i=0;i<numPlayers;i++){
            if (PlayerName.equalsIgnoreCase(playerNames[i])){
                    indexOfPlayer=i;
                    break;
            }
        }
        
        for(int j=0;j<shipsNames.length;j++){
            
            if(shipName.equalsIgnoreCase(shipsLocations[indexOfPlayer][j].getShipName())){
                x[0]=shipsLocations[indexOfPlayer][j].getFrontOfShip().getRow();
                y[0]=shipsLocations[indexOfPlayer][j].getFrontOfShip().getColumn();
                direction[0]=shipsLocations[indexOfPlayer][j].getWayOfPlacement();

                break;
            }  
        }

    }  

     /**
     * Convert a way of placement string to an integer
     * @param wayOfPlacement Horizontal or Vertical
     * @return wayOfPlacement-returns integer. Invalid if neither Horizontal or Vertical were given
     */
    
    private int wayOfPlacementStrToInt(String wayOfPlacement){

        int way=Ship.INVALID;

        if ("HORIZONTAL".equalsIgnoreCase(wayOfPlacement))
            way= Ship.HORIZONTAL;
        
        else if ("VERTICAL".equalsIgnoreCase(wayOfPlacement))
            way= Ship.VERTICAL;
        
        
        return way;
       
    }
    
    /**
     * This function returns the number of appearances of a string in a string array
     * @param name
     * @param index bound of array
     * @param StrArr
     * @return number of appearances
     */
    private int numOfAppearances(String name,int index,String []StrArr){
        int numOfApp=0;
        for(int k=0;k<(index+1) && numOfApp<=1;k++){
            if(name.equalsIgnoreCase(StrArr[k]))
                numOfApp++;
         }
                    

         return numOfApp;    
    }
    
     /**
     * Read the board inputs from the xml file
     */
    private void  ReadBoardFromXml() {
        

        try {
            File fXmlFile = new File("src\\Ui\\GameConfigXML.xml");
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            
            dbFactory.setValidating(true);
            dbFactory.setNamespaceAware(true);
            
            dbFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", 
                                    "http://www.w3.org/2001/XMLSchema");

            
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            
            //do not handle the errors, just throw error
            dBuilder.setErrorHandler(null);
            
            Document doc = dBuilder.parse(fXmlFile);
            doc.getDocumentElement().normalize();
            
            //
            //players info
            //
            NodeList playersList = doc.getElementsByTagName("player");
            numPlayers=playersList.getLength();
            playerNames = new String [numPlayers];
            playerTypes = new int [numPlayers];
            String playerType;
            
            for (int p = 0; p < playersList.getLength(); p++) { 
                 Node playerNode = playersList.item(p);
                 NamedNodeMap playerAttributes = (NamedNodeMap)playerNode.getAttributes();
                 Attr playerAttribute;

                            
                //name value            
                playerAttribute = (Attr)playerAttributes.item(0);
                        
                 if (playerAttribute.getNodeType()==Node.ATTRIBUTE_NODE){
                     if (checkIfNameValid(playerAttribute.getValue(),p,playerNames))
                        playerNames[p] =  playerAttribute.getValue(); 
                     else 
                         throw new Error("One of the player names is not valid in the XML(either empty or duplicate). Try again.");
                 }
                    
                 //type value
                 playerAttribute = (Attr)playerAttributes.item(1);
                 
                 if (playerAttribute.getNodeType()==Node.ATTRIBUTE_NODE){
                     playerType=playerAttribute.getValue();
                     playerType.toUpperCase();
                     

                     if (playerType.equalsIgnoreCase("HUMAN") )
                        playerTypes[p]=1;
                     else if ( playerType.equalsIgnoreCase("COMPUTER") )
                        playerTypes[p]=2; 
                     else
                         throw new Error("The type of one ore more of the players is not right in the XML.Try Again");
                         
 
                 }
            }

            
            
            
            //
            //ships info
            //
            NodeList shipsList = doc.getElementsByTagName("boat");
            
            shipsNames = new String [shipsList.getLength()];
            shipSizesArray=new int [shipsList.getLength()];
            
            if(shipsList.getLength()!=5)
                throw new Error("There neeeds to be 5 declared ships in the XML file.Try Again...");

            for (int b = 0; b < shipsList.getLength(); b++) {
                Node shipNode = shipsList.item(b);
                NamedNodeMap shipAttributes = (NamedNodeMap)shipNode.getAttributes();
                Attr shipAttribute;

                 //ship name
                shipAttribute = (Attr)shipAttributes.item(0);

                 if (shipAttribute.getNodeType()==Node.ATTRIBUTE_NODE){
                     if (checkIfNameValid(shipAttribute.getValue(),b,shipsNames))
                        shipsNames[b] = shipAttribute.getValue(); 
                     else 
                         throw new Error("One or more of the ship names is not valid in the XML(either empty or duplicate). Try again.");
                 }
                    
                 
                 
                 //ship size
                 shipAttribute = (Attr)shipAttributes.item(1);
                 
                 if (shipAttribute.getNodeType()==Node.ATTRIBUTE_NODE)
                    shipSizesArray[b] = Integer.parseInt(shipAttribute.getValue());
  
            }
            
            
            //
            //boards info
            //
            NodeList boardsList = doc.getElementsByTagName("board");
            
            String playerName;
            int num=0;

            NamedNodeMap boardAttributes;
            Attr boardAttribute;
            
            if (numPlayers!=(boardsList.getLength()))
                throw new Error("The number of Players doesn't match the number of boards in the XML. Try again...");
            
            String [] boardPlayerNames= new String [boardsList.getLength()];
            
            shipsLocations=new Ship[boardsList.getLength()][shipsList.getLength()];
            
            for (int b = 0; b < boardsList.getLength(); b++) {
                Node boardNode = boardsList.item(b);
                boardAttributes = (NamedNodeMap)boardNode.getAttributes();
                
                //get first and only attribute-the players name
                boardAttribute = (Attr)boardAttributes.item(0);

                
                if (boardAttribute.getNodeType()==Node.ATTRIBUTE_NODE){
                    
                    playerName=boardAttribute.getValue();
                    boardPlayerNames[b]=playerName;


                    int numberOfAppearances=numOfAppearances(playerName,b,boardPlayerNames);
     
                    if (numberOfAppearances!=1)
                        throw new Error("The player names attributes don't match the player names in board attributes in the XML.Try again");
                        
         
                   
                    
                    //search for player index
                    int playerIndexInArr=-1;
                    for(int j=0;j<playerNames.length;j++){
                        if(playerName.equalsIgnoreCase(playerNames[j])){
                            playerIndexInArr=j;
                            break;

                        }
                    }
                    
                   if (playerIndexInArr==-1)
                        throw new Error("The player names attributes don't match the player names in board attributes in the XML.Try again");

                  
                 
                    
                    NodeList locationList = doc.getElementsByTagName("location");
                    int numLocations=(locationList.getLength()/numPlayers);
                    
                    if (shipsNames.length!=numLocations)
                        throw new Error("The number of ships available doesn't match the number of locations. Try again...");
                    
                    
                    String shipName="";
                    String shipPosition="";
                    String shipDirection="";
                    
                    String [] boardShipNames= new String [numLocations];
                    
                    for (int k = 0; k < numLocations; k++) {
                        Node locationNode = locationList.item(k+num);
                        NamedNodeMap locationAttributes = (NamedNodeMap)locationNode.getAttributes();
                        Attr locationAttribute;
                        
                        for(int g=0;g<locationAttributes.getLength();g++){
                            
                            
                            locationAttribute = (Attr)locationAttributes.item(g);
                            if (locationAttribute.getNodeType()==Node.ATTRIBUTE_NODE){
                               
                                String name = locationAttribute.getName();


                                if (name.equalsIgnoreCase("name")){
                                    shipName=locationAttribute.getValue();
                                    boardShipNames[k]=shipName;
                                }

                                else if( name.equalsIgnoreCase("direction")){
                                    shipDirection=locationAttribute.getValue();
                                    if ( wayOfPlacementStrToInt(shipDirection)==Ship.INVALID )
                                         throw new Error("Theres a ship direction that is incorrect in the XML.Try again");

                            
                                }

                                else if (name.equalsIgnoreCase("position")){
                                    shipPosition=locationAttribute.getValue();
                                    Coordinate invalidCoord=new Coordinate(-1,-1);
                                    if (convertStrCoordinateToInt(shipPosition).equals(invalidCoord))
                                         throw new Error("Theres a ship position that is incorrect in the XML.Try again");

                                }

                                
                            }

                        }
                        
                        
                        int numbOfAppearances=numOfAppearances(shipName,k,boardShipNames);
     
                        if (numbOfAppearances!=1)
                            throw new Error("The ship names attributes don't match the ship names in board attributes in the XML.Try again");

                        
                        
                        int shipIndexInArr=-1;
                        
                        //search for ship to get its size
                        for(int j=0;j<shipsNames.length;j++){
                            if(shipName.equalsIgnoreCase(shipsNames[j])){
                                shipIndexInArr=j;
                                break;
                            }
                        }
                        
                        if (shipIndexInArr==-1)
                            throw new Error("The ship names attributes don't match the ship names in board attributes in the XML.Try again");

                        
                        
                        
                        Coordinate coordinate=convertStrCoordinateToInt(shipPosition);

                        //create new ship

                        shipsLocations[playerIndexInArr][k]= new Ship(coordinate,shipSizesArray[shipIndexInArr],shipName,wayOfPlacementStrToInt(shipDirection));
                        
                        

                    }
                    
                   
                }
                num=num+5;
            }
            
        }
        
        catch (Exception e) {
            throw new Error("Something in the XML file is not valid,please try again...");     
        }

    }
    

    
}
